package com.security.config.security.authentication.username;

import com.security.domain.po.Account;
import com.security.service.impl.AccountServiceImpl;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

/**
 * 用户名密码认证逻辑
 * 实现 AuthenticationProvider 接口，重写 authenticate 方法
 * 当类型为 UsernameAuthenticationToken 的认证实体进入时才走此 Provider
 * 认证成功后返回 UsernameAuthenticationToken 类型的认证实体
 *
 * @author DblSun
 */
public class UsernameAuthenticationProvider implements AuthenticationProvider {
    private final AccountServiceImpl accountService;
    private final BCryptPasswordEncoder bCryptPasswordEncoder;

    public UsernameAuthenticationProvider(AccountServiceImpl accountService,
                                          BCryptPasswordEncoder bCryptPasswordEncoder) {
        this.accountService = accountService;
        this.bCryptPasswordEncoder = bCryptPasswordEncoder;
    }

    /**
     * 认证逻辑
     */
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        UsernameAuthenticationToken usernameAuthenticationToken = (UsernameAuthenticationToken) authentication;
        Account account = (Account) accountService
                .loadUserByUsername(usernameAuthenticationToken.getPrincipal().toString());
        if (!bCryptPasswordEncoder.matches(usernameAuthenticationToken.getCredentials().toString(), account.getPassword())) {
            throw new BadCredentialsException("用户名或密码错误");
        }
        if (!account.isEnabled() || !account.isAccountNonLocked() || !account.isAccountNonExpired()) {
            throw new LockedException("账号已被禁用");
        }
        // 返回认证成功的 UsernameAuthenticationToken ，内含授权信息
        return new UsernameAuthenticationToken(account, account.getPassword(), account.getAuthorities());
    }

    /**
     * 当类型为 UsernameAuthenticationToken 的认证实体进入时才走此 Provider
     */
    @Override
    public boolean supports(Class<?> authentication) {
        return UsernameAuthenticationToken.class.isAssignableFrom(authentication);
    }
}
